/*
 * RoundRectangle.java  0.4.0 / Oct 24, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing.graphics;

import java.util.NoSuchElementException;

/**
 * Round Rectangle Shape.
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public class RoundRectangle extends RectangularShape {

// Private Fields. -------------------------------------------------------------

  private final Rectangle bounds;
  private final int arcwidth;
  private final int archeight;
  private int hashCode; // NOPMD

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new RoundRectangle object.
   */
  public RoundRectangle() {
    bounds = new Rectangle();
    arcwidth = 0;
    archeight = 0;
  }

  /**
   * Constructs new RoundRectangle object.
   *
   * @param x the x coordinate of the rectangle
   * @param y the y coordinate of the rectangle
   * @param width the width of the rectangle
   * @param height the height of the rectangle
   * @param arcwidth the arc width
   * @param archeight the arc height
   */
  public RoundRectangle(int x, int y, int width, int height, int arcwidth,
                        int archeight) {
    bounds = new Rectangle(x, y, width, height);
    this.arcwidth = arcwidth;
    this.archeight = archeight;
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Returns the arc width.
   *
   * @return the arc width
   */
  public int getArcWidth() {
    return arcwidth;
  }

  /**
   * Returns the arc height.
   *
   * @return the arc height
   */
  public int getArcHeight() {
    return archeight;
  }

// Shape Interface Implementation. ---------------------------------------------

  /**
   * {@inheritDoc}
   */
  public boolean contains(int x, int y) {
    double rx = getWidth() >> 1;
    double ry = getHeight() >> 1;
    double tx = (x - (getX() + rx)) / rx;
    double ty = (y - (getY() + ry)) / ry;
    return (((tx * tx) + (ty * ty)) < 1.0);
  }

  /**
   * {@inheritDoc}
   */
  public Rectangle getBounds() {
    return bounds;
  }

  /**
   * {@inheritDoc}
   */
  public PathIterator getPathIterator() {
    return new RoundRectanglePathIterator(this);
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    }
    if ((obj != null) && (obj.getClass() == getClass())) {
      RoundRectangle r = (RoundRectangle)obj;
      return ((r.getX() == getX()) && (r.getY() == getY()) &&
              (r.getWidth() == getWidth()) && (r.getHeight() == getHeight()) &&
              (r.getArcWidth() == getArcWidth()) &&
              (r.getArcHeight() == getArcHeight()));
    }
    return false;
  }

  /**
   * {@inheritDoc}
   */
  public int hashCode() {
    if (hashCode == 0) {
      int hashCode = 127;
      hashCode = 439 * hashCode + getX();
      hashCode = 439 * hashCode + getY();
      hashCode = 439 * hashCode + getWidth();
      hashCode = 439 * hashCode + getHeight();
      hashCode = 439 * hashCode + getArcWidth();
      hashCode = 439 * hashCode + getArcHeight();
      this.hashCode = hashCode;
    }
    return hashCode;
  }

  /**
   * {@inheritDoc}
   */
  public String toString() {
    return getClass().getName() + "[x=" + getX() + ",y=" + getY() + ",width=" +
           getWidth() + ",height=" + getHeight() + ",arcwidth=" +
           getArcWidth() + ",archeight=" + getArcHeight() + "]";
  }

  /**
   * {@inheritDoc}
   */
  public int getX() {
    return bounds.getX();
  }

  /**
   * {@inheritDoc}
   */
  public int getY() {
    return bounds.getY();
  }

  /**
   * {@inheritDoc}
   */
  public int getWidth() {
    return bounds.getWidth();
  }

  /**
   * {@inheritDoc}
   */
  public int getHeight() {
    return bounds.getHeight();
  }

// Inner Classes. --------------------------------------------------------------

  /**
   * Round Rectangle Path Iterator.
   */
  protected static class RoundRectanglePathIterator implements PathIterator {

    private final int x;
    private final int y;
    private final int width;
    private final int height;
    private final int arcWidth;
    private final int arcHeight;
    private int iteration;

    /**
     * Constructs new RoundRectanglePathIterator object.
     *
     * @param r the round rectangle
     */
    public RoundRectanglePathIterator(RoundRectangle r) {
      x = r.getX();
      y = r.getY();
      width = r.getWidth();
      height = r.getHeight();
      int arcWidth = r.getArcWidth();
      int arcHeight = r.getArcHeight();
      if (arcWidth < 0) {
        arcWidth = -arcWidth;
      }
      if (width < arcWidth) {
        arcWidth = width;
      }
      this.arcWidth = arcWidth;
      if (arcHeight < 0) {
        arcHeight = -arcHeight;
      }
      if (height < arcHeight) {
        arcHeight = height;
      }
      this.arcHeight = arcHeight;
      if ((arcWidth < 0) || (arcHeight < 0)) {
        iteration = 10;
      }
    }

    /**
     * {@inheritDoc}
     */
    public int getWindingRule() {
      return WIND_NON_ZERO;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isDone() {
      return (iteration > 9);
    }

    /**
     * {@inheritDoc}
     */
    public void next() {
      iteration++;
    }

    /**
     * {@inheritDoc}
     */
    public int currentSegment(int[] coords2) {
      switch (iteration) {
        case 0:
          coords2[0] = x;
          coords2[1] = y + (arcHeight >> 1);
          return SEG_MOVETO;
        case 1:
          coords2[0] = x;
          coords2[1] = y + height - (arcHeight >> 1);
          return SEG_LINETO;
        case 2:
          coords2[0] = x;
          coords2[3] = y + height;
          coords2[1] = coords2[3] - (int)(0.22385762508460333 * arcHeight);
          coords2[2] = x + (int)(0.22385762508460333 * arcWidth);
          coords2[4] = x + (arcWidth >> 1);
          coords2[5] = coords2[3];
          return SEG_CUBICTO;
        case 3:
          coords2[0] = x + width - (arcWidth >> 1);
          coords2[1] = y + height;
          return SEG_LINETO;
        case 4:
          coords2[2] = x + width;
          coords2[0] = coords2[2] - (int)(0.22385762508460333 * arcWidth);
          coords2[1] = y + height;
          coords2[3] = coords2[1] - (int)(0.22385762508460333 * arcHeight);
          coords2[4] = coords2[2];
          coords2[5] = coords2[1] - (int)(0.5 * arcHeight);
          return SEG_CUBICTO;
        case 5:
          coords2[0] = x + width;
          coords2[1] = y + (arcHeight >> 1);
          return SEG_LINETO;
        case 6:
          coords2[0] = x + width;
          coords2[1] = y + (int)(0.22385762508460333 * arcHeight);
          coords2[2] = coords2[0] - (int)(0.22385762508460333 * arcWidth);
          coords2[3] = y;
          coords2[4] = coords2[0] - (arcWidth >> 1);
          coords2[5] = y;
          return SEG_CUBICTO;
        case 7:
          coords2[0] = (int)(x + 0.5 * arcWidth);
          coords2[1] = y;
          return SEG_LINETO;
        case 8:
          coords2[0] = x + (int)(0.22385762508460333 * arcWidth);
          coords2[1] = y;
          coords2[2] = x;
          coords2[3] = y + (int)(0.22385762508460333 * arcHeight);
          coords2[4] = x;
          coords2[5] = y + (arcHeight >> 1);
          return SEG_CUBICTO;
        case 9:
          return SEG_CLOSE;
        default:
          throw new NoSuchElementException(
                        "Round Rectangle Path Iterator out of bounds");
      }
    }

  }

}
